 #--
#   Television Episode Renamer
#   - Initial Milestone: April 2nd, 2008
#       - Find URL from epguides.com
#       - filenames to rename_list.csv
#   - Internal Milestone 1: April 2nd, 2008 to April 5th, 2008
#       - construct new name based on Template
#       - correctly interact with rename_list, make batch/sh/etc and better
#   - Internal Milestone 2: April 5th, 2008 to April 6th, 2008
#       - search an epguides.txt file and html page and fill out ep title
#   - Internal Milestone 3: April 6th, 2008 - April 8th, 2008
#       - option to walk directory structure from given location
#       - optparse add and support
#   - Internal Milestone 4: April 8th, 2008 to April 10th, 2008
#       - optionize more assumptions /
#       - handle showname/Season # /
#       - more judicious printing - eg: handle folder as a batch? /
#       - seperate verbose and debug print /
#       - decide "normal" printouts /
#       - reduce O(n) where obvious /
#       - interactive/batch mode /
#   - Internal Milestone 5: April 10th, 2008 - April 16th, 2008
#       - tv.com support, website (/), cache (/)
#       - splitting up a complete series folder, when numbered in air order /
#       - support .html versions as file /
#   - Internal Milestone 6: April 16th, 2008 - April 19th, 2008
#       - Handles double episodes, based on splitChar
#       - Much better control flow, safer overwrites
#       - Correct and better command line options
#       - can use the tv.com all episodes summary page to rename a full run
#       - tv.com txt file support
#   - Eventual Goals
#       - Nice to Have
#           - create html page with each file's link to recap
#               + page has link to dvd/show site image, saved to dir as folder.jpg
#           - implement caching (/), saving source site to plain text
#           - preferences file
#           - add new sources -> pull sources from preference file
#           - pull the recap from epguides/other guides (twop) to dir
#           - interaction with TED/miro/other open source players
#       - Epguides Support
#           - investigate consequence of assuming dot{28}
#       - Non-standard numbering
#           - clip shows, miniseries
#           - British conventions
#               + Series "names" as in Blackadder Goes Fourth
#       - Interaction
#           - create a simple breakpoint function for debug
#       - Files/Folders organization issues
#           - get/confirm show name from the filename
#           - for complete series in one folder, split it into seperate folders
#           - fully handle folder schemes that aren't too weird
#           - Option to move files into new folders
#
#   Rob Dennis  - Lurchington Q. Delacroix ESQ. and man about town
#
#   This script is inspired and is thanks to: 
#   -   Rob Meerman's tvrenamer perl script
#           http://www.robmeerman.co.uk/coding/file_renamer
#   -   Softpointer's Tag&Rename
#           http://www.softpointer.com/tr
#   
#--

import os,re,glob,time,sys,string,shutil,urllib2
from string import Template

from optparse import OptionParser

unparsedFiles = []
unfoundWebsites = []
collisionFiles = []

osRenameCommand = "mv"
undoBatchName = "undoBatch.sh"
nameBatchName = "nameBatch.sh"
batchHeaderString = r"#!/bin/bash"

parser = OptionParser(version="%prog - Internal Milestone 6 - Start: April 16th, 2008 - April 19th, 2008")

parser.add_option("-u","--makeUndoBatchFile",dest="undoBatchFlag",action="store_true",
                  help="enable creation of naming batch file [default: %default]")

parser.add_option("-U","--noMakeUndoBatchFile",dest="undoBatchFlag",action="store_false",
                  help="disable creation of naming batch file")

parser.add_option("-n","--makeNamingBatchFile",dest="nameBatchFlag",action="store_true",
                  help="enable creation of undo batch file [default: %default]")

parser.add_option("-N","--noMakeNamingBatchFile",dest="nameBatchFlag",action="store_false",
                  help="disable creation of undo batch file")

parser.add_option("-w","--writeCacheToFile",dest="writeCacheToFileFlag",action="store_true",
                  help="writes webpage cache to text file in dir [default: %default]")

parser.add_option("-W","--noWriteCacheToFile",dest="writeCacheToFileFlag",action="store_false",
                  help="does not write webpage cache to text file in dir")

parser.add_option("--quashBatchEntries",dest="quashBatchEntriesFlag",action="store_true",
                  help="always overwrite batch file [default: %default]")

parser.add_option("--noQuashBatchEntries",dest="quashBatchEntriesFlag",action="store_false",
                  help="only add new entries to batch file")

parser.add_option("-p","--pretend",dest="pretendFlag",action="store_true",
                  help="do not make any changes [default: %default]")

parser.add_option("-c","--confirm",dest="pretendFlag",action="store_true",
                  help="make changes where desired")

parser.add_option("-m","--guessMultipart",dest="guessMultipartFlag",action="store_true",
                  help="will not read Part 1 in title as episode number 1 [default: %default]")

parser.add_option("-M","--noGuessMultipart",dest="guessMultipartFlag",action="store_false",
                  help="will not try to guess multi-part episodes from filename")

parser.add_option("--handleFullRun",dest="handleFullRunFlag",action="store_true",
                  help="for episodes numbered with the overall show order [default: %default]")

parser.add_option("--noHandleFullRun",dest="handleFullRunFlag",action="store_false",
                  help="no special handling for overall show order")

parser.add_option("-s","--splitFullRun",dest="splitFullRunFlag",action="store_true",
                  help="split full run into season and episode [default: %default]")

parser.add_option("-S","--noSplitFullRun",dest="splitFullRunFlag",action="store_false",
                  help="don't split full run into season and episode")

parser.add_option("--guessShowNameFlag",dest="guessShowFromNameFlag",action="store_true",
                  help="deals with showname in fileName (unwieldy)")

parser.add_option("--noGuessShowNameFlag",dest="guessShowFromNameFlag",action="store_false",
                  help="does not attenmpt to deal with shawname in filename")

parser.add_option("-a","--assumeGroupedNum",dest="assumeGroupedNumFlag",action="store_true",
                  help="treats fields like 1005 as both season and episode number [default: %default]")

parser.add_option("-A","--noAssumeGroupedNum",dest="assumeGroupedNumFlag",action="store_false",
                  help="treats fields like 1005 as one number")

parser.add_option("-g","--groupNumSplit",dest="groupNumSplit",metavar="NUM",type="int",
                  help="when handling group numbers, split after NUM places [default: %default]")

parser.add_option("-G","--episodePadding",dest="episodePadding",metavar="NUM",type="int",
                  help="pads episode number to NUM places [default: %default]")

parser.add_option("--seasonPadding",dest="seasonPadding",metavar="NUM",type="int",
                  help="pads season number to NUM places [default: %default]")

parser.add_option("--splitEpisodeChar",dest="splitEpisodeChar",metavar="CHAR",
                  help="Detects numbers split by CHAR as multi-episodic [default: %default]")

parser.add_option("--useUnixNaming",dest="useUnixNamingFlag",action="store_true",
                  help="replaces whitespace with '_' [default: %default]")

parser.add_option("--noUseUnixNaming",dest="useUnixNamingFlag",action="store_false",
                  help="no special handling for whitespace")

parser.add_option("-i","--allowPartialInfo",dest="allowPartialInfoFlag",action="store_true",
                  help="allow partially unknown information in new filename [default: %default]")

parser.add_option("-I","--noAllowPartialInfo",dest="allowPartialInfoFlag",action="store_false",
                  help="any unknown information will default the script to old filename")

parser.add_option("-r","--recursiveRename",dest="recursiveRenameFlag",action="store_true",
                  help="recursively rename files in all subdirectory [default: %default]")

parser.add_option("-R","--noRecursiveRename",dest="recursiveRenameFlag",action="store_false",
                  help="limit renaming to current directory")

parser.add_option("-d","--debug",dest="debugFlag",action="store_true",
                  help="print extra debug output [default: %default]")

parser.add_option("-D","--noDebug",dest="debugFlag",action="store_false",
                  help="do not print extra debug output")

parser.add_option("-v","--verbose",dest="verboseFlag",action="store_true",
                  help="print verbose output [default: %default]")

parser.add_option("-V","--noVerbose",dest="verboseFlag",action="store_false",
                  help="do not print verbose output")

parser.add_option("-q","--quiet",dest="quietFlag",action="store_true",
                  help="squelch all output [default: %default]")

parser.add_option("-Q","--noQuiet",dest="quietFlag",action="store_false",
                  help="print all output")

parser.add_option("-t","--topLevel",dest="topLevelDir",metavar="PATH",
                  help="top level rename hierarchy [default: current working directory]")

parser.add_option("--unattended",dest="unattendedFlag",action="store_true",
                  help="do not prompt for input [default: %default]")

parser.add_option("--interactive",dest="unattendedFlag",action="store_false",
                  help="require prompts for input")

parser.add_option("-o","--outputNameString",dest="outputNameString",metavar="STRING",
                  help="rename filename to formatted STRING [default: %default]")

parser.add_option("--showname",dest="manualShowname",
                  help="manually specify the showname to use [default: %default]")

parser.add_option("--season",dest="manualSeason",
                  help="manually specify the season to use [default: %default]")

parser.set_defaults(
                    undoBatchFlag=True,
                    nameBatchFlag=False,
                    writeCacheToFileFlag=False,
                    quashBatchEntriesFlag=False,
                    pretendFlag=False,
                    guessMultipartFlag=True,
                    handleFullRunFlag=True,
                    splitFullRunFlag=True,
                    guessMultipartFlag=True,
                    handleFullRunFlag=True,
                    splitFullRunFlag=True,
                    guessShowFromNameFlag=True,
                    assumeGroupedNumFlag=True,
                    groupNumSplit=2,
                    episodePadding=2,
                    seasonPadding=2,
                    splitEpisodeChar="-",
                    useUnixNamingFlag=False,
                    allowPartialInfoFlag=True,
                    recursiveRenameFlag=True,
                    debugFlag=False,
                    verboseFlag=False,
                    topLevelDir=os.getcwd(),
                    unattendedFlag=False,
                    outputNameString=r"${showName} - S${seasonNum}E${episodeNum} - ${episodeTitle}",
                    manualShowname=None,
                    manualSeason=None)

originalCwd=os.getcwd()

tvComTxt= {"name":"TV.com textfile","isFile":True,"isSite":False,"filename":"tvcom.txt","syntax":"tvComTxt"}
tvComCacheHTML = {"name":"TV.com Webpage Cache","isFile":True,"isSite":False,"filename":"tvcom.html","syntax":"tvComHTML"}
tvComWebHTML = {"name":"TV.com Webpage","isFile":False,"isSite":True,"URL":r"http://www.tv.com/search.php?type=11&stype=program&qs=","syntax":"tvComHTML","cacheFile":tvComCacheHTML}
epguidesTxt= {"name":"Epguides textfile","isFile":True,"isSite":False,"filename":"epguides.txt","syntax":"epguidesTxt"}
epguidesCacheHTML= {"name":"Epguides Webpage Cache","isFile":True,"isSite":False,"filename":"epguides.html","syntax":"epguidesHTML"}
epguidesWebHTML= {"name":"Epguides Webpage","isFile":False,"isSite":True,"URL":r"http://www.epguides.com/","syntax":"epguidesHTML","cacheFile":epguidesCacheHTML}

(opt,arg) = parser.parse_args()

def dPrnt(string,flag=opt.debugFlag,left="<dPrnt-:",right="->"):
    if flag and (opt.verboseFlag and not opt.quietFlag):
        print left+str(string)+right

def vPrnt(string,flag=opt.verboseFlag,left="{vPrnt-:",right="-}"):
    if flag and not opt.quietFlag:
        print left+str(string)+right

def qPrnt(string,flag=opt.quietFlag,left="",right=""):
    if not flag:
        print left+str(string)+right        

def checkRegexList(regexList,label="",pFlag=True):
    for x in range(len(regexList)):
        if regexList[x]:
            returnVal = regexList[x].group(0).strip()
            if pFlag:
                dPrnt("%sRE%i: %s" %(label,x+1,returnVal))
            return returnVal,x+1
    if pFlag:
        dPrnt("%s not parsed in string"%label)
    return "UNK","UNK"

informationSources= [epguidesTxt,epguidesCacheHTML,epguidesWebHTML,tvComTxt,tvComCacheHTML,tvComWebHTML]

if opt.useUnixNamingFlag:
    whitespaceToUnderscore = string.maketrans(" ","_")

if opt.manualSeason or opt.manualShowname:
    qPrnt("manually specifying information, no recursion allowed")
    opt.recursiveRenameFlag = False

def renameFileList(inList):
    unRenamedLines = []
    source = []
    dest = []
    count = 0
    
    dPrnt("cwd: %s" % os.getcwd())
    parentDir=os.path.split(os.getcwd())[1].strip()
    parentOfParentDir=os.path.split(os.path.split(os.getcwd())[0])[1].strip()
    dPrnt("Parent of Parent Dir: %s" % parentOfParentDir)
    dPrnt("Parent Dir: %s" % parentDir)

    newNames = reshapeNames(inList,parentDir,parentOfParentDir)

    qPrnt("Attempting to make the following changes")
    qPrnt("----------------------------------------")
    for x in range(len(inList)):
        if newNames[x]!=inList[x]:
            qPrnt("%s %s %s" %(osRenameCommand,inList[x],newNames[x]))
            if not opt.pretendFlag:
                source.append(inList[x])
                dest.append(newNames[x])
            count = count + 1
        else:
            unRenamedLines.append("%s"%inList[x])
    if count == 0:
        qPrnt("no files changed")
        qPrnt("----------------------------------------")
        return None

    qPrnt("----------------------------------------")

    if opt.verboseFlag:
        vPrnt("renaming %i files"%count)

        
    if not opt.quietFlag and len(unRenamedLines):
        print "Unnamed files:|"
        print "--------------+"
        for untouchedFile in unRenamedLines:
           print "\t- %s" % untouchedFile

    prompt=""

    while not opt.unattendedFlag and not len(prompt)>0:
        prompt=raw_input("really make change? (y/n): ")

    if opt.unattendedFlag or re.match(prompt[0],"y",re.IGNORECASE):
        for i in range(len(source)):
            if os.path.exists(dest[i]):
                collisionFile.append("%s -> %s"%(source[i],dest[i]))
                if not unattendedFlag:
                    prompt=raw_input("overwrite %s? (y/n): "%dest[i])
                else:
                    prompt="n"

            if not os.path.exists(dest[i]) or re.match(prompt[0],"y",re.IGNORECASE):
                if not opt.pretendFlag:
                    shutil.move(source[i],dest[i])        

    if (opt.undoBatchFlag):
        makeNamingBatchFile(undoBatchName,osRenameCommand,dest,source)
    if (opt.nameBatchFlag):
        makeNamingBatchFile(nameBatchName,osRenameCommand,source,dest)


def makeNamingBatchFile(fileName,command,source,dest):
    vPrnt("creating batch file")
    if opt.quashBatchEntriesFlag:
        vPrnt("overwriting current batch")
    if len(source) != len(dest):
        qPrnt("mismatched rename lists batch create failed!")
    else:
        sourceNot=[]
        destNot=[]
        if os.path.exists(fileName):
            inF=open(fileName,"rb")

            if not opt.quashBatchEntriesFlag:
                modeString="ab"
                for line in inF.readlines():
                    if len(line)>1:
                        if line[0] != "#":
                            lineS=line.split()[1:]
                            if len(lineS)>1 and (lineS[1] in dest):
                                dPrnt(lineS[1]+"matched, dropped from file")
                                x=dest.index(lineS[1])
                                destNot.append(dest[x])
                                sourceNot.append(source[x])
                inF.close()
                
            else:
                modeString="wb"
        elif not opt.pretendFlag:
            modeString="wb"
            os.system("touch %s" %fileName)


        if not opt.pretendFlag:
            outF=open(fileName,modeString)

            if modeString=="wb":
                outF.write(batchHeaderString+"\n")            
        
        for x in range(len(source)):
            if not opt.pretendFlag:
                if source[x] not in sourceNot and dest[x] not in destNot:
                    if source[x]!=dest[x]:
                        outF.write("%s %s %s\n" %(command,source[x],dest[x]))
                    else:
                        dPrnt("batch source and dest match for %s, dropping line" % source[x])
        if not opt.pretendFlag:
            outF.close()
            

def reshapeNames(oldNameList,parentDir,parentOfParentDir,outputNameString=opt.outputNameString):
    dPrnt("in reshapeNames")
    
    nameTemplate=Template(outputNameString)
    newNameList=[]

    for oldName in oldNameList:
        show,season,episode,title=searchInfo(oldName,parentDir,parentOfParentDir)
        extension=oldName[oldName.rfind("."):]
        if not opt.allowPartialInfoFlag:
            if show=="UNK" or season=="UNK" or episode=="UNK" or title=="UNK":
                vPrnt("returning old name due to undefined information")
                unparsedFiles.append("%s/%s/%s"%(parentOfParentDir,parentDir,oldName))
                newName=oldName

            else:
                newName=nameTemplate.substitute(showName=str(show),seasonNum=str(season),
                                            episodeNum=str(episode),
                                            episodeTitle=str(title))+extension
 
        else:
            if episode=="UNK" or title=="UNK":
                dPrnt("appending old name to prevent information loss")
                unparsedFiles.append("%s/%s/%s"%(parentOfParentDir,parentDir,oldName))
                newName=nameTemplate.substitute(showName=str(show),seasonNum=str(season),
                                            episodeNum=str(episode),
                                            episodeTitle=str(title))+"-"+oldName
            else:
                dPrnt("appending old name to prevent information loss")
                unparsedFiles.append("%s/%s/%s"%(parentOfParentDir,parentDir,oldName))
                newName=nameTemplate.substitute(showName=str(show),seasonNum=str(season),
                                            episodeNum=str(episode),
                                            episodeTitle=str(title))+extension
                
        #deleting illegal filename characters
        newName=re.sub(r"[\!\?\$\&\*\^\"\:]","",newName)
        newName=re.sub(r"[\\]","-",newName)
        qPrnt(newName)
            
        if opt.useUnixNamingFlag:
            newName=newName.translate(whitespaceToUnderscore)
            
        newNameList.append(newName)
        
    return newNameList

def searchInfo(fileN,parentDir,parentOfParentDir):
    dPrnt("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%")
    dPrnt("in searchInfo")
    vPrnt("+------------------------")
    vPrnt("| - fileN: %s" % fileN)
    vPrnt("+------------------------")
    
#- Getting tv show name from folder
    
    parentDir=os.path.split(os.getcwd())[1].strip()
    parentOfParentDir=os.path.split(os.path.split(os.getcwd())[0])[1].strip()

    dPrnt("%s -> "%parentDir)
    parentDir=re.sub(r"[^A-Za-z0-9]"," ",parentDir)
    #replace non alpha-numerics with whitespace
    dPrnt("-> %s "%parentDir)

    shownameIndex=0

    if not opt.manualShowname:
        shownameRegexList = []
        #shownameRE1
        shownameRegexList.append(re.search(r".*(?=(Season|Series))",parentDir,re.IGNORECASE))
        #shownameRE2
        shownameRegexList.append(re.search(r".*?(?=s\d+)",parentDir,re.IGNORECASE))
        #shownameRE3
        shownameRegexList.append(re.search(r"(\w|\S)*?(?=(\d))",parentDir,re.IGNORECASE))
        #shownameRE4
        shownameRegexList.append(re.search(r".*",parentDir,re.IGNORECASE))

        showname,correctRE = checkRegexList(shownameRegexList,"showname")

        if len(showname)==0:
            dPrnt("using parent of parent directory as show name")
            showname=parentOfParentDir
    else:
        showname = opt.manualShowname

    if opt.guessShowFromNameFlag:
        shownameFileRE1,correctRE=checkRegexList([re.search(r"[A-Z]+(?=[\ _\.])",fileN,re.IGNORECASE)],"shownameFile1")
        shownameFileRE2,correctRE=checkRegexList([re.search(r"(?<=[\. _]).+?(?=[A-Z\ \._][0-9])",fileN,re.IGNORECASE)],"shownameFile2")

        shownameWordList=showname.split()

        dPrnt("| checking showname in filename")
        dPrnt("+ fileN: %s" % fileN)
        dPrnt("+ showN: %s" % showname)
        dPrnt("shownameFileRE1: %s" % shownameFileRE1)
        dPrnt("shownameFileRE2: %s" % shownameFileRE2)
        dPrnt("---")
        dPrnt(str(shownameWordList))
        dPrnt("---")

        shownameIndex=fileN.find(shownameWordList[-1])

        if shownameIndex<0:
            shownameIndex=0

        shownameIndex=shownameIndex+len(shownameWordList[-1])

        dPrnt("Found %s in %s"%(shownameWordList[-1],fileN))
        dPrnt("Leaving: %s"%fileN[shownameIndex:])     
        
#- Getting season number from filename
    seasonRet="UNK"
    episodeRet="UNK"

    if not opt.manualSeason:
        seasonRegexList=[]

        #seasonRE1
        seasonRegexList.append(re.search(r"^[0-9]+",fileN[shownameIndex:],re.I))
        #seasonRE2
        seasonRegexList.append(re.search(r"(?<=(Season)[\ \._])[0-9]*",fileN[shownameIndex:],re.I))
        #seasonRE3
        seasonRegexList.append(re.search(r"(?<=[\ _xs\.])[0-9]+",fileN[shownameIndex:],re.I))
        #seasonRE4
        seasonRegexList.append(re.search(r"(?<=[\ _xs\.])[0-9]+",parentDir,re.I))
        
        seasonRet,correctRE=checkRegexList(seasonRegexList,"season")

        if seasonRet!="UNK":
            seasonIndex=fileN.find(seasonRet)+len(seasonRet)
        else:
            seasonIndex=0

        if correctRE==4:
            seasonIndex=0
                      
        if seasonRet != "UNK" and opt.assumeGroupedNumFlag:
            dPrnt("trying to split episode")
            if len(seasonRet)>opt.groupNumSplit:
                episodeRet=seasonRet[-opt.groupNumSplit:]
                seasonRet=seasonRet[:-opt.groupNumSplit]
                dPrnt("splitting value into s:%s and leaving:%s "%(seasonRet,fileN[seasonIndex:]))
            else:
                dPrnt("---")
                dPrnt("index set at: %d, leaving: %s "%(seasonIndex,fileN[seasonIndex:]))
                dPrnt("---")
    else:
        if opt.manualSeason=="Full":
            seasonRet="UNK"
        else:
            seasonRet=opt.manualSeason
        seasonIndex=shownameIndex
                
            
#- Getting episode number from filename

    if episodeRet == "UNK":
        episodeRegexList = []

        episodeRegexList.append(re.search(r"(?<=e])[0-9]+",fileN,re.I))
        episodeRegexList.append(re.search(r"(?<=[\ _xe\.])[0-9]+",fileN[seasonIndex:],re.I))
        episodeRegexList.append(re.search(r"[0-9]+",fileN[seasonIndex:],re.I))
        
        episodeRet,correctRE=checkRegexList(episodeRegexList,"episode")

        dPrnt(re.findall(r"(?<=\D)[0-9]+(?=\D)",fileN[seasonIndex:]))


    if episodeRet!="UNK" and opt.guessMultipartFlag and len(re.findall(r"(?<=\D)[0-9]+(?=\D)",fileN[seasonIndex:]))>0:
        dPrnt("check for multipart episode read as show number")
        dPrnt("originally thought episodeRet was: %s"%episodeRet)
        multipartCheckList=[r"(part|p|pt)[\. 0]*%s"%str(int(episodeRet[:opt.episodePadding])),
                                r"[{\[].*%s.*[}\]]"%str(int(episodeRet[:opt.episodePadding]))]

        for checkRegex in multipartCheckList:
            if re.search(checkRegex,fileN,re.IGNORECASE):
                dPrnt("found %s in filename, assuming multipart episode"%re.search(checkRegex,fileN,re.IGNORECASE).group(0))
                episodeRet="UNK"
                
    if episodeRet == "UNK" and seasonRet != "UNK" and re.search(r"[0-9]+",fileN,re.I):
        dPrnt("assuming only number in filename is episode")

        dPrnt("using folder name to find season number")
        episodeRet=seasonRet

        seasonRegexList = []
        seasonRegexList.append(re.search(r"(?<=[\ _xs\.])[0-9]+",parentDir,re.I))

        seasonRet,correctRE=checkRegexList(seasonRegexList,"seasonRedo")

    if episodeRet!="UNK":
        dPrnt("checking for double episodes")

        episodeDoubleList = []
        episodeDoubleList.append(re.search(r"%s%s[0-9]+"%(episodeRet,opt.splitEpisodeChar),fileN,re.I))

        episodeDouble,correctRE=checkRegexList(episodeDoubleList,"episodeDouble")

        if episodeDouble != "UNK":
            dPrnt("found double episode number: %s"%episodeDouble)
            episodeRet=episodeDouble
        else:
            dPrnt("detecting single episode only")

#- Getting the episde title from a source
    episodeHolder=""
    titleHolder=""
    
    if episodeRet !="UNK" and showname != "UNK":
        for episodeSplit in episodeRet.split(opt.splitEpisodeChar):
            for source in informationSources:     
                #this handles double episode numbers
                dPrnt("checking split episode %s"%episodeSplit)
                titlePiece,seasonRet,episodePiece=getTitleFromSource(source,showname,seasonRet,episodeSplit)
                dPrnt("returned titlePiece: %s and episodePiece %s"%(titlePiece,episodePiece))

                if titlePiece!="UNK":
                    if episodePiece.isdigit():
                        episodePiece=episodePiece.zfill(opt.episodePadding)
                    episodeHolder=episodeHolder+opt.splitEpisodeChar+episodePiece
                    titleHolder=titleHolder+(" %s ")%opt.splitEpisodeChar+titlePiece
                    break
    else:
        title="UNK"

    if seasonRet.isdigit():
        seasonRet=seasonRet.zfill(opt.seasonPadding)

    episodeRet=episodeHolder[1:]
    title=titleHolder[3:]
        
    vPrnt("+------------------------")
    vPrnt("| - [showname]: %s [seasonRet]: %s [episodeRet]: %s [title]: %s" %(showname,seasonRet,episodeRet,title))
    vPrnt("+------------------------")

    return showname,seasonRet,episodeRet,title
        
def getTitleFromSource(source,showname,season,episode):
# + --------------
# - Getting source information to sourceDump
# + --------------

    dPrnt(("getting title from %s")%source["name"])
    newEpisode=episode
    newSeason=season

    

    if source["isSite"]:
        dPrnt("which is at %s"%source["URL"])
        if source["syntax"]=="epguidesHTML":
            norm = string.maketrans("","")
            searchTerm=showname.translate(norm,string.whitespace+"'")
            if re.match("The",searchTerm,re.I):
                searchTerm=searchTerm[3:]

            searchTerm=re.sub("&","And",searchTerm)
            pullPage=source["URL"]+searchTerm
            vPrnt("searching %s"%pullPage)

        if source["syntax"]=="tvComHTML":
            if season=="UNK":
                dPrnt("unknown season, returning unknown title")
                return "UNK",season,episode
     
            searchTerm=re.sub(" ","%20",showname)
            splashPage=source["URL"]+searchTerm

            if source.has_key(splashPage):
                dPrnt("loaded %s from cache"%splashPage)
                splashPageDump = source[splashPage]
            else:
                dPrnt("caching %s"%splashPage)
                try:
                    splashPageDump = urllib2.urlopen(splashPage).read()
                except:
                    dPrnt("no page found, continuing search")
                    source[splashPage]=None
                    unfoundWebsites.append("---\n%s\n%s\n---\n" %(os.getcwd(),splashPage))
                    return "UNK",season,episode
                
                source[splashPage]=splashPageDump

            if not splashPageDump:
                dPrnt("cached results show unknown show name")
                return "UNK",season,episode

            shownameDash=re.sub(" ","-",showname)
            tvComNumberMatch=re.search(r"(?<=(http://www.tv.com/%s/show/))[0-9]*(?=(/summary.html?))"%shownameDash,splashPageDump,re.IGNORECASE+re.MULTILINE)
            if tvComNumberMatch:
                dPrnt("splashpage finds tvCom number: %s"%tvComNumberMatch.group(0))
                tvComNumber=tvComNumberMatch.group(0)
            else:
                dPrnt("search not yielding results for %s at tv.com"%showname)
                return "UNK",season,episode

            if season.isdigit():
                pullPage=r"http://www.tv.com/%s/show/%s/episode.html?season=%s&tag=season_dropdown;dropdown;0"%(shownameDash,tvComNumber,str(int(season)))
            else:
                dPrnt("non numerical season, returning unknown title")
                return "UNK",season,episode
 
        if source.has_key(pullPage):
            sourceDump=source[pullPage]
            dPrnt("%s information loaded from %s" %(searchTerm,source["syntax"]))
        else:
            try:
                sourceDump=urllib2.urlopen(pullPage).read()
                
            except:
                dPrnt("no page found, continuing search")
                source[pullPage]=None

                unfoundWebsites.append("---\n%s\n%s\n---\n" %(os.getcwd(),pullPage))
                
                return "UNK",season,episode
            dPrnt("%s information saved to %s" %(showname,source["syntax"]))
            source[pullPage]=sourceDump

            if opt.writeCacheToFileFlag:
                cacheFilename=source["cacheFile"]["filename"]
                dPrnt("Writing cachefile to %s"%cacheFilename)
                outF=open(cacheFilename,"wb")
                outF.write(sourceDump)
                outF.close()

        if not sourceDump:
            dPrnt("cached results show unknown show name")
            return "UNK",season,episode

    elif source["isFile"]:
        vPrnt("which is %s"%source["filename"])
        if os.path.exists(source["filename"]):
            sourceDump=open(source["filename"],"rb").read()
        else:
            dPrnt("%s does not exist"%source["filename"])
            return "UNK",season,episode

    else:
        dPrnt("unknown source type")
        return "UNK",season,episode

# + --------------
# - Now operating on sourcePage by constructing regular expressions
# + --------------

    dPrnt("searching for %s in sourcePage"%showname)
    dPrnt("Using [showname]: %s [season]: %s [episode]: %s" %(showname,season,episode))

    if season == "UNK" and episode != "UNK":
        if opt.handleFullRunFlag:   #full run syntax
            dPrnt("attempting to use broadcast order")

            # epguides.com copy and paste format
            if source["syntax"]=="epguidesTxt":
                lineTemplate=Template(r"(?<=${episodeT}\.).*")
                lineTemplate=lineTemplate.substitute(episodeT=str(int(episode)).rjust(3))
                
                dPrnt("lineTemplate: %s"%lineTemplate)

                lineRE=re.compile(lineTemplate,re.IGNORECASE+re.MULTILINE)

                lineMatch=re.search(lineRE,sourceDump)

                if lineMatch:
                    dPrnt("found broadcast number %s"%str(episode))

                    if opt.splitFullRunFlag:
                        dPrnt("attempting to discern season-episode split")
                        line=lineMatch.group(0)
                        dPrnt(line)
                        newSeason=re.search(r"[0-9]*(?=-)",line).group(0)
                        newEpisode=re.search(r"(?<=-)[ ]?[0-9]*",line).group(0)
                        dPrnt("guessing info as season:%s episode:%s"%(newSeason,newEpisode))
                        
                    sourceDump=lineMatch.group(0)
                    titleRE=re.compile(r"(?<=[A-Za-z]{3} [0-9]{2} ).*",re.IGNORECASE)
                else:
                    dPrnt("step broadcast 1: title not parsed from %s"%source["name"])
                    return "UNK",season,episode

            # tv.com copy and paste format
            if source["syntax"]=="tvComTxt":

                sourceDump,correctRE=checkRegexList([re.search(r"(?<=All Seasons\nNo\.).*",sourceDump,re.IGNORECASE+re.DOTALL)],"allDump",False)
         
                lineTemplate=Template(r"${episodeT}[ ]+.*")
                lineTemplate=lineTemplate.substitute(episodeT=str(int(episode)))
                
                dPrnt("lineTemplate: %s"%lineTemplate)

                lineRE=re.compile(lineTemplate,re.IGNORECASE+re.MULTILINE)
                lineMatch=re.search(lineRE,sourceDump)

##                prompt=raw_input("testBreak")

                if lineMatch:
                    dPrnt("found broadcast number %s"%str(episode))

                    sourceDump=lineMatch.group(0)
                    titleRE=re.compile(r"(?<= ).*?(?=[0-9])",re.IGNORECASE)

                    dPrnt("%s"%sourceDump)

                else:
                    dPrnt("step broadcast 1: title not parsed from %s"%source["name"])
##                    prompt=raw_input("failbreak")
                    return "UNK",season,episode
##                prompt=raw_input("passBreak")

            else:
                dPrnt("invalid braodcast run source syntax,%s specified by %s"%(source["syntax"],source["name"]))
                return "UNK",season,episode
                        
        else:
            dPrnt("returning unknown title due to unknown season")
            return "UNK",season,episode
    else:   #non-full run syntax
        # epguides.com html format
        if source["syntax"]=="epguidesHTML":
            searchTemplate=Template(r"(?<=${seasonT}-${episodeT}.{28}).*")
            searchTemplate=searchTemplate.substitute(seasonT=str(int(season)).rjust(2),episodeT=str(int(episode)).rjust(2))
            lineRE=re.compile(searchTemplate,re.IGNORECASE+re.MULTILINE)
            
            lineMatch=lineRE.search(sourceDump)

            if lineMatch:
                sourceDump=lineMatch.group(0)
                titleRE=re.compile(r"(?<=>).*?(?=<)")
            else:
                dPrnt("step 1: title not parsed from %s"%source["name"])
                return "UNK",season,episode

            
        # epguides.com copy and paste format
        elif source["syntax"]=="epguidesTxt":
            sval=str(int(season)).rjust(2)
            epval=str(int(episode)).rjust(2)
            searchTemplate=Template(r"(?<=${seasonT}-${episodeT}.{28}).*")
            searchTemplate=searchTemplate.substitute(seasonT=sval,episodeT=epval)
            titleRE=re.compile(searchTemplate,re.IGNORECASE+re.MULTILINE)

        elif source["syntax"]=="tvComHTML":
            sourceDumpL = re.findall(r"<h1 class=\"f-18 f-666\">\s*?\d+.*?</a>",sourceDump,re.DOTALL+re.IGNORECASE+re.MULTILINE)
            if int(episode)-1 < len(sourceDumpL):
                sourceDump=sourceDumpL[int(episode)-1]
            else:
                dPrnt("no results found in sourceDumpL")
                return "UNK",season,episode
                
            titleRE=re.compile(r"(?<=>).*?(?=<)")

        elif source["syntax"]=="tvComTxt":
            sourceDump = re.findall(r"^(?:(?:[0-9]+?\.)|Pilot|Special).*?(?=Community Score)",sourceDump,re.DOTALL+re.MULTILINE+re.IGNORECASE)[int(episode)-1]
            print sourceDump
            sourceDump=re.sub("Pilot|Special",str(episode)+".",sourceDump)
            titleRE=re.compile(r"(?<=\.).*")
               
        else:
            dPrnt("invalid source syntax,%s specified by %s"%(source["syntax"],source["name"]))
            return "UNK",season,episode

# + --------------
# - Now operating on sourcePage by evaluating regular expression
# + --------------

    titleMatch = titleRE.search(sourceDump)
    
    if titleMatch:
        if opt.splitFullRunFlag:
            return titleMatch.group(0).strip(),newSeason.strip(),newEpisode.strip()
        else:
            return titleMatch.group(0)
    else:
        dPrnt("title not parsed from %s"%source["name"])
        return "UNK",season,episode



def main():
    startTime=time.time()
    
    fileTypes = re.compile(r'.*(\.avi|mkv|\.mpg|\.rm|\.flv)$',re.IGNORECASE)

    os.chdir(opt.topLevelDir)
    
    for root,dirs,fileNameList in os.walk(opt.topLevelDir):
        qPrnt("checking %s" %root)
        os.chdir(root)
        
        if len(fileNameList)>0:
            newFileNameList = []

            for fileN in fileNameList:
                if fileTypes.match(fileN):
                    newFileNameList.append(fileN)

            listLength=len(newFileNameList)

            vPrnt("Attempting to rename %s files" % listLength)

            if listLength>0:
                renameFileList(newFileNameList)

                prompt=""
                while not opt.unattendedFlag and not len(prompt)>0:
                    prompt=raw_input("continue? (y/n): ")
                
                if not re.match(prompt[0],"y",re.IGNORECASE):
                    break            
            else:
                vPrnt("no usable files found in %s" % root)

            qPrnt(time.time()-startTime)
            qPrnt("---")

        if not opt.recursiveRenameFlag:
            break

main()
os.chdir(originalCwd)

if not opt.quietFlag:
    qPrnt("--------------------")
    qPrnt("unParsedFiles")
    for x in unparsedFiles:
        print x
    qPrnt("--------------------")
    qPrnt("unfoundWebsites")
    for x in unfoundWebsites:
        print x
    qPrnt("--------------------")
    qPrnt("collidedFiles")
    for x in collisionFiles:
        print x
    qPrnt("--------------------")
